#include "stdafx.h"
#include "StratumRender.h"

glm::vec4 HSV(float h, float s, float v)
{
	float c = v * s;
	float h2 = h / 60;
	float x = c * (1 - abs((h2 - floor(h2 / 2)) - 1));
	float r, g, b;

	if(h2 >= 0 && h2 < 1)
	{
		r = c; g = x; b = 0;
	}
	else if(h2 >= 1 && h2 < 2)
	{
		r = x; g = c; b = 0;
	}
	else if(h2 >= 2 && h2 < 3)
	{
		r = 0; g = c; b = x;
	}
	else if(h2 >= 3 && h2 < 4)
	{
		r = 0; g = x; b = c;
	}
	else if(h2 >= 4 && h2 < 5)
	{
		r = x; g = 0; b = c;
	}
	else if(h2 >= 5 && h2 < 6)
	{
		r = c; g = 0; b = x;
	}

	r += (v - c);
	g += (v - c);
	b += (v - c);

	return glm::vec4(r, g, b, 1);
}

const UINT uiCircleVertexCount = 10;

CStratumRender::CStratumRender(CRenderContext* pContext)
	: m_pRenderContext(pContext)
	, m_bRebuildNeeded(TRUE)
	, m_pDrillData(NULL)
	, m_pPorosityData(NULL)
	, m_uiPorosityCount(0)
	, m_pSaturationData(NULL)
	, m_uiSaturationCount(0)
	, m_eInterpolateMode(EInterpolateModeIDW)
	, m_fDrillRadius(500)
	, m_uiMeshWidth(10)
	, m_uiMeshHeight(10)
	, m_uiMeshDepthOrigin(10)
	, m_uiMeshDepth(0)
	, m_stBackgroundColor(0, 0, 0, 1)
	, m_bTransformBegined(FALSE)
	, m_pDrillVertexBuffer(NULL)
	, m_uiDrillVertexCount(0)
	, m_pStratumVertexBuffer1(NULL)
	, m_pStratumVertexBuffer2(NULL)
	, m_uiStratumVertexCount(0)
	, m_fMaxDensity(0)
{
}

CStratumRender::~CStratumRender()
{
	DestroyModel();
}

HRESULT CStratumRender::SetData(
	const CArray2D<glm::vec3>* pDrillData, 
	glm::vec4* pPorosityData, UINT uiPorosityCount, 
	glm::vec4* pSaturationData, UINT uiSaturationCount)
{
	if(!pDrillData || !pPorosityData || !pSaturationData)return E_INVALIDARG;

	m_pDrillData = pDrillData;
	m_pPorosityData = pPorosityData;
	m_uiPorosityCount = uiPorosityCount;
	m_pSaturationData = pSaturationData;
	m_uiSaturationCount = uiSaturationCount;

	m_bRebuildNeeded = TRUE;

	return S_OK;
}

HRESULT CStratumRender::SetInterpolateMode(EInterpolateMode eMode)
{
	m_eInterpolateMode = eMode;
	m_bRebuildNeeded = TRUE;
	return S_OK;
}

HRESULT CStratumRender::SetDrillRadius(float fRadius)
{
	m_fDrillRadius = fRadius;
	m_bRebuildNeeded = TRUE;
	return S_OK;
}

HRESULT CStratumRender::SetMeshDimension(UINT uiWidth, UINT uiHeight, UINT uiDepth)
{
	m_uiMeshWidth = uiWidth;
	m_uiMeshHeight = uiHeight;
	m_uiMeshDepthOrigin = uiDepth;
	m_bRebuildNeeded = TRUE;
	return S_OK;
}

HRESULT CStratumRender::SetBackgroundColor(COLORREF dwColor)
{
	m_stBackgroundColor.r = GetRValue(dwColor) / 255.0f;
	m_stBackgroundColor.g = GetGValue(dwColor) / 255.0f;
	m_stBackgroundColor.b = GetBValue(dwColor) / 255.0f;
	return S_OK;
}

HRESULT CStratumRender::BuildModel(ProgressCallback pCallback, LPVOID pContext)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	DestroyModel();

	//get bounding box
	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiDrillCount = cDrillData.GetHeight();
	UINT uiStratumCount = cDrillData.GetWidth();

	m_stBoundBoxMin = cDrillData[0][0];
	m_stBoundBoxMax = cDrillData[0][0];

	for(UINT i = 0; i < uiDrillCount; ++i)
	{
		for(UINT j = 0; j < uiStratumCount; ++j)
		{
			m_stBoundBoxMin = glm::min(cDrillData[i][j], m_stBoundBoxMin);
			m_stBoundBoxMax = glm::max(cDrillData[i][j], m_stBoundBoxMax);
		}
	}

	m_uiMeshDepth = m_uiMeshDepthOrigin / m_pDrillData->GetWidth();

	//fill drill vertex and index buffer
	FillDrillVertexBuffer();
	if(pCallback)pCallback(25, pContext);
	FillDrillModels();
	if(pCallback)pCallback(50, pContext);
	FillStratumVertexBuffer();
	if(pCallback)pCallback(75, pContext);
	FillStratumModels();
	if(pCallback)pCallback(100, pContext);

	m_bRebuildNeeded = FALSE;

	return S_OK;
}

HRESULT CStratumRender::ResetPosition()
{
	m_stWorldMatrix = glm::mat4();
	return S_OK;
}

HRESULT CStratumRender::BeginTransform()
{
	m_bTransformBegined = TRUE;
	m_stOriginWorldMatrix = m_stWorldMatrix;
	return S_OK;
}

HRESULT CStratumRender::Translate(float fX, float fY, float fZ)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(!m_bTransformBegined)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	glm::mat4 stIdentityMatrix;
	glm::mat4 stTranslateMatrix;
	stTranslateMatrix = glm::translate(
		stIdentityMatrix, 
		glm::vec3(fX, fY, fZ));
	m_stWorldMatrix = stTranslateMatrix * m_stOriginWorldMatrix;

	return S_OK;
}

HRESULT CStratumRender::Scale(float fX, float fY, float fZ)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(!m_bTransformBegined)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	glm::vec3 stCenter = GetCenter();
	glm::vec4 stTemp;
	glm::mat4 stIdentityMatrix;
	glm::mat4 stScaleMatrix;

	stTemp = m_stOriginWorldMatrix * glm::vec4(stCenter.x, stCenter.y, stCenter.z, 1);
	stCenter = glm::vec3(stTemp.x, stTemp.y, stTemp.z);
	
	stScaleMatrix = glm::scale(
		stIdentityMatrix, 
		glm::vec3(fX, fY, fZ));

	m_stWorldMatrix = 
		glm::translate(stIdentityMatrix, stCenter) * 
		stScaleMatrix * 
		glm::translate(stIdentityMatrix, -stCenter) * 
		m_stOriginWorldMatrix;

	return S_OK;
}

HRESULT CStratumRender::Rotate(float fX, float fY, float fZ)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(!m_bTransformBegined)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	glm::vec3 stCenter = GetCenter();
	glm::vec4 stTemp;
	glm::mat4 stIdentityMatrix;
	glm::mat4 stRotateMatrix;

	stTemp = m_stOriginWorldMatrix * glm::vec4(stCenter.x, stCenter.y, stCenter.z, 1);
	stCenter = glm::vec3(stTemp.x, stTemp.y, stTemp.z);

	stRotateMatrix = glm::rotate(
		stIdentityMatrix, 
		fX, 
		glm::vec3(1, 0, 0));

	stRotateMatrix = glm::rotate(
		stIdentityMatrix, 
		fY, 
		glm::vec3(0, 1, 0)) * stRotateMatrix;

	m_stWorldMatrix = 
		glm::translate(stIdentityMatrix, stCenter) * 
		stRotateMatrix * 
		glm::translate(stIdentityMatrix, -stCenter) * 
		m_stOriginWorldMatrix;

	return S_OK;
}

HRESULT CStratumRender::EndTransform()
{
	m_bTransformBegined = FALSE;
	return S_OK;
}

HRESULT CStratumRender::ClearScreen()
{
	glm::mat4 stWorldViewProj(
		1, 0, 0, 0, 
		0, 1, 0, 0, 
		0, 0, 1, 0, 
		0, 0, 0, 1);

	m_pRenderContext->Begin(stWorldViewProj);

	glClearColor(
		m_stBackgroundColor.r, 
		m_stBackgroundColor.g, 
		m_stBackgroundColor.b, 
		m_stBackgroundColor.a);
	glClear(GL_COLOR_BUFFER_BIT);

	m_pRenderContext->End();

	m_pRenderContext->SwapBffers();

	return S_OK;
}

HRESULT CStratumRender::RenderDrills(ERenderMode eMode, const std::vector<bool>& cVisibilities)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(m_bRebuildNeeded)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	glm::mat4 stWorldViewProj = GetWorldViewProjMatrix();

	m_pRenderContext->Begin(stWorldViewProj);

	glShadeModel(GL_SMOOTH);
	glDisable(GL_CULL_FACE);
	glDisable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
	glClearColor(
		m_stBackgroundColor.r, 
		m_stBackgroundColor.g, 
		m_stBackgroundColor.b, 
		m_stBackgroundColor.a);
	glClearDepth(1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
	glEnableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());

	UINT uiDrillCount = m_pDrillData->GetHeight();
	for(UINT uiDrill = 0; uiDrill < uiDrillCount; ++uiDrill)
	{
		if(!cVisibilities[uiDrill] || !m_cDrillModels[uiDrill].pIndexBuffers[eMode])continue;
		glVertexAttribPointer(
			m_pRenderContext->GetPositionAttribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), m_pDrillVertexBuffer);
		glVertexAttribPointer(
			m_pRenderContext->GetDiffuseAtrribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), ((float*)m_pDrillVertexBuffer) + 4);

		glDrawElements(
			eMode == ERenderModeMesh ? GL_LINES : GL_TRIANGLES, 
			m_cDrillModels[uiDrill].uiIndexCounts[eMode], 
			GL_UNSIGNED_INT, 
			m_cDrillModels[uiDrill].pIndexBuffers[eMode]);
	}

	RenderBoundBox();

	glDisableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
	glDisableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());

	glDisable(GL_DEPTH_TEST);

	m_pRenderContext->End();

	m_pRenderContext->SwapBffers();

	return S_OK;
}

HRESULT CStratumRender::RenderStratums(ERenderMode eMode, const std::vector<bool>& cVisibilities, BOOL bShadeByDensity)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(m_bRebuildNeeded)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	glm::mat4 stWorldViewProj = GetWorldViewProjMatrix();

	m_pRenderContext->Begin(stWorldViewProj);

	glShadeModel(GL_SMOOTH);
	glDisable(GL_CULL_FACE);
	glDisable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
	glClearColor(
		m_stBackgroundColor.r, 
		m_stBackgroundColor.g, 
		m_stBackgroundColor.b, 
		m_stBackgroundColor.a);
	glClearDepth(1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
	glEnableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());

	UINT uiStratumCount = m_pDrillData->GetWidth();
	for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
	{
		if(!cVisibilities[uiStratum] || !m_cStratumModels[uiStratum].pIndexBuffers[eMode])continue;
		glVertexAttribPointer(
			m_pRenderContext->GetPositionAttribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), 
			bShadeByDensity ? m_pStratumVertexBuffer2 : m_pStratumVertexBuffer1);
		glVertexAttribPointer(
			m_pRenderContext->GetDiffuseAtrribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), 
			((float*)(bShadeByDensity ? m_pStratumVertexBuffer2 : m_pStratumVertexBuffer1)) + 4);

		glDrawElements(
			eMode == ERenderModeMesh ? GL_LINES : GL_TRIANGLES, 
			m_cStratumModels[uiStratum].uiIndexCounts[eMode], 
			GL_UNSIGNED_INT, 
			m_cStratumModels[uiStratum].pIndexBuffers[eMode]);
	}

	RenderBoundBox();

	glDisableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
	glDisableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());

	glDisable(GL_DEPTH_TEST);

	m_pRenderContext->End();

	if(bShadeByDensity)
	{
		RenderLegend();
	}

	m_pRenderContext->SwapBffers();

	return S_OK;
}

HRESULT CStratumRender::GetDrillColor(UINT uiDrill, COLORREF& dwColor)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(uiDrill >= m_pDrillData->GetHeight()) return E_INVALIDARG;

	glm::vec4 stColor = HSV(uiDrill * 360.0f / m_pDrillData->GetHeight(), 1.0f, 1.0f);
	dwColor = RGB(int(stColor.r * 255), int(stColor.g * 255), int(stColor.b * 255));

	return S_OK;
}

HRESULT CStratumRender::GetStratumColor(UINT uiStratum, COLORREF& dwColor)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(uiStratum >= m_pDrillData->GetWidth()) return E_INVALIDARG;

	glm::vec4 stColor = HSV(uiStratum * 360.0f / m_pDrillData->GetWidth(), 1.0f, 1.0f);
	dwColor = RGB(int(stColor.r * 255), int(stColor.g * 255), int(stColor.b * 255));

	return S_OK;
}

HRESULT CStratumRender::GetResourceAmount(float* pfAmount)
{
	if(!m_pDrillData)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(m_bRebuildNeeded)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
	if(!pfAmount)return E_INVALIDARG;

	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiDrillCount = cDrillData.GetHeight();
	UINT uiStratumCount = cDrillData.GetWidth();
	float fAmount = 0;

	CPropertyProvider cPorosityProvider(m_pPorosityData, m_uiPorosityCount);
	CPropertyProvider cSaturationProvider(m_pSaturationData, m_uiSaturationCount);
	IInterpolator* pPorosityInterpolator = CreateInterpolator();
	IInterpolator* pSaturationInterpolator = CreateInterpolator();
	pPorosityInterpolator->Initialize(&cPorosityProvider);
	pSaturationInterpolator->Initialize(&cSaturationProvider);

	float fCellWidth = (m_stBoundBoxMax.x - m_stBoundBoxMin.x) / m_uiMeshWidth;
	float fCellHeight = (m_stBoundBoxMax.y - m_stBoundBoxMin.y) / m_uiMeshHeight;

	for(UINT uiStratum = 0; uiStratum < uiStratumCount - 1; ++uiStratum)
	{
		UINT uiStratumVertexStart = uiStratum * (m_uiMeshWidth + 1) * (m_uiMeshHeight + 1);
		UINT uiNextStratumVertexStart = uiStratumVertexStart + (m_uiMeshWidth + 1) * (m_uiMeshHeight + 1);

		for(UINT i = 0; i < m_uiMeshHeight; ++i)
		{
			for(UINT j = 0; j < m_uiMeshWidth; ++j)
			{
				SRenderVertex stMaxVertex = m_pStratumVertexBuffer1[uiStratumVertexStart + i * (m_uiMeshWidth + 1) + j];
				SRenderVertex stMinVertex = m_pStratumVertexBuffer1[uiNextStratumVertexStart + i * (m_uiMeshWidth + 1) + j];
				float fCellDepth = (stMaxVertex.stPosition.z - stMinVertex.stPosition.z) / m_uiMeshDepth;

				for(UINT k = 0; k < m_uiMeshDepth; ++k)
				{
					glm::vec4 stPosition(
						m_stBoundBoxMin.x + fCellWidth * j, 
						m_stBoundBoxMin.y + fCellHeight * i, 
						stMaxVertex.stPosition.z - fCellDepth * k, 
						1);
					float fDensity = GetStratumDensity(
						pPorosityInterpolator, 
						pSaturationInterpolator, 
						stPosition);
					fAmount += fCellWidth * fCellHeight * fCellDepth * fDensity;
				}
			}
		}
	}

	delete pPorosityInterpolator;
	delete pSaturationInterpolator;

	*pfAmount = fAmount;
	return S_OK;
}

glm::vec3 CStratumRender::GetCenter()
{
	glm::vec3 stCenter = (m_stBoundBoxMin + m_stBoundBoxMax) / 2.0f;
	glm::vec3 stEye = stCenter + (glm::vec3(m_stBoundBoxMin.x, stCenter.y, stCenter.z) - stCenter) * 5.0f;
	glm::vec3 stUp(0, 0, 1);
	glm::mat4 stView = glm::lookAt(stEye, stCenter, stUp);

	glm::vec4 stTemp = stView * glm::vec4(stCenter.x, stCenter.y, stCenter.z, 1);
	return glm::vec3(stTemp.x, stTemp.y, stTemp.z);
}

glm::mat4 CStratumRender::GetWorldViewProjMatrix()
{
	HWND hPreviewWnd = m_pRenderContext->GetPreviewWnd();
	RECT rcPreview;
	::GetClientRect(hPreviewWnd, &rcPreview);

	glm::vec3 stCenter = (m_stBoundBoxMin + m_stBoundBoxMax) / 2.0f;
	glm::vec3 stEye = stCenter + (glm::vec3(m_stBoundBoxMin.x, stCenter.y, stCenter.z) - stCenter) * 5.0f;
	glm::vec3 stUp(0, 0, 1);
	glm::mat4 stView = glm::lookAt(stEye, stCenter, stUp);
	//glm::vec3 stEye = stCenter + (glm::vec3(stCenter.x, stCenter.y, m_stBoundBoxMax.z) - stCenter) * 3.0f;
	//glm::mat4 stView = glm::lookAt(stEye, stCenter, glm::vec3(0, 1, 0));
	glm::mat4 stProjection = glm::perspective(
		90.0f, 
		(rcPreview.right - rcPreview.left) * 1.0f / (rcPreview.bottom - rcPreview.top), 
		100.0f, //!!!! 1.0f will cause flicker
		glm::length(m_stBoundBoxMax - m_stBoundBoxMin) * 3.0f);

	return stProjection * m_stWorldMatrix * stView;
}

void CStratumRender::FillDrillVertexBuffer()
{
	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiDrillCount = cDrillData.GetHeight();
	UINT uiStratumCount = cDrillData.GetWidth();
	
	//fill vertex buffer
	m_pDrillVertexBuffer = new SRenderVertex[uiDrillCount * uiStratumCount * (uiCircleVertexCount + 1)];
	m_uiDrillVertexCount = 0;
	for(UINT uiDrill = 0; uiDrill < uiDrillCount; ++uiDrill)
	{
		for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
		{
			glm::vec3 stCenter = cDrillData[uiDrill][uiStratum];
			m_pDrillVertexBuffer[m_uiDrillVertexCount].stPosition = glm::vec4(stCenter.x, stCenter.y, stCenter.z, 1);
			m_pDrillVertexBuffer[m_uiDrillVertexCount++].stDiffuse = HSV(uiDrill * 360.0f / uiDrillCount, 1.0f, 1.0f);;
			for(UINT k = 0; k < uiCircleVertexCount; ++k)
			{
				glm::vec3 v = stCenter;
				SRenderVertex sVertex;
				v.x += m_fDrillRadius * cos(k * 2 * PI / uiCircleVertexCount);
				v.y += m_fDrillRadius * sin(k * 2 * PI / uiCircleVertexCount);
				sVertex.stPosition = glm::vec4(v.x, v.y, v.z, 1);
				sVertex.stDiffuse = HSV(uiDrill * 360.0f / uiDrillCount, 1.0f, 1.0f);
				m_pDrillVertexBuffer[m_uiDrillVertexCount++] = sVertex;
			}
		}
	}
}

void CStratumRender::FillDrillModels()
{
	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiDrillCount = cDrillData.GetHeight();
	UINT uiStratumCount = cDrillData.GetWidth();

	for(UINT uiDrill = 0; uiDrill < uiDrillCount; ++uiDrill)
	{
		SModel sModel;
		::ZeroMemory(&sModel, sizeof(sModel));

		//layer mode
		sModel.pIndexBuffers[ERenderModeLayer] = new int[uiStratumCount * (uiCircleVertexCount * 3)];
		for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
		{
			#define DRILL_LAYER_INDEX sModel.pIndexBuffers[ERenderModeLayer][sModel.uiIndexCounts[ERenderModeLayer]++]

			UINT uiCircleStart = uiStratumCount * (uiCircleVertexCount + 1) * uiDrill + (uiCircleVertexCount + 1) * uiStratum;
			for(UINT k = 0; k < uiCircleVertexCount; ++k)
			{	
				DRILL_LAYER_INDEX = uiCircleStart;
				DRILL_LAYER_INDEX = uiCircleStart + 1 + k;
				DRILL_LAYER_INDEX = uiCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
			}
		}

		//mesh and solid mode
		sModel.pIndexBuffers[ERenderModeMesh] = new int[uiStratumCount * (uiCircleVertexCount * 9) * 2];
		sModel.pIndexBuffers[ERenderModeSolid] = new int[uiStratumCount * (uiCircleVertexCount * 9)];
		for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
		{
			#define DRILL_MESH_INDEX sModel.pIndexBuffers[ERenderModeMesh][sModel.uiIndexCounts[ERenderModeMesh]++]
			#define DRILL_SOLID_INDEX sModel.pIndexBuffers[ERenderModeSolid][sModel.uiIndexCounts[ERenderModeSolid]++]
			UINT uiFirstCircleStart = uiStratumCount * (uiCircleVertexCount + 1) * uiDrill + (uiCircleVertexCount + 1) * uiStratum;
			UINT uiSecondCircleStart = uiFirstCircleStart + (uiCircleVertexCount + 1);

			for(UINT k = 0; k < uiCircleVertexCount; ++k)
			{
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart;
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart + 1 + k;
				DRILL_MESH_INDEX = uiFirstCircleStart + 1 + k;
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiFirstCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiFirstCircleStart;
			}

			if(uiStratum == uiStratumCount - 1)break;

			for(UINT k = 0; k < uiCircleVertexCount; ++k)
			{
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart + 1 + k;
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiSecondCircleStart + 1 + k;
				DRILL_MESH_INDEX = uiSecondCircleStart + 1 + k;
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiSecondCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiSecondCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiFirstCircleStart + 1 + k;

				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart + 1 + k;
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiSecondCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiSecondCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = DRILL_SOLID_INDEX = uiFirstCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiFirstCircleStart + 1 + ((k + 1) % uiCircleVertexCount);
				DRILL_MESH_INDEX = uiFirstCircleStart + 1 + k;
			}
		}

		m_cDrillModels.push_back(sModel);
	}
}

void CStratumRender::FillStratumVertexBuffer()
{
	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiStratumCount = cDrillData.GetWidth();
	float fCellWidth = (m_stBoundBoxMax.x - m_stBoundBoxMin.x) / m_uiMeshWidth;
	float fCellHeight = (m_stBoundBoxMax.y - m_stBoundBoxMin.y) / m_uiMeshHeight;

	CPropertyProvider cPorosityProvider(m_pPorosityData, m_uiPorosityCount);
	CPropertyProvider cSaturationProvider(m_pSaturationData, m_uiSaturationCount);
	IInterpolator* pPorosityInterpolator = new CIDWInterpolator(); //CreateInterpolator();
	IInterpolator* pSaturationInterpolator = new CIDWInterpolator(); //CreateInterpolator();
	pPorosityInterpolator->Initialize(&cPorosityProvider);
	pSaturationInterpolator->Initialize(&cSaturationProvider);

	m_pStratumVertexBuffer1 = new SRenderVertex[((uiStratumCount - 1) * (m_uiMeshDepth + 1) + 1) * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1)];
	m_pStratumVertexBuffer2 = new SRenderVertex[((uiStratumCount - 1) * (m_uiMeshDepth + 1) + 1) * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1)];
	m_uiStratumVertexCount = 0;

	m_fMaxDensity = 0;

	//precalculating all vertex positions
	glm::vec3* pTempVertexPos = new glm::vec3[uiStratumCount * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1)];
	UINT uiTempVertexCount = 0;
	for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
	{
		CZProvider cProvider(cDrillData, uiStratum);
		IInterpolator* pInterpolator = CreateInterpolator();
		pInterpolator->Initialize(&cProvider);
		for(UINT i = 0; i <= m_uiMeshHeight; ++i)
		{
			for(UINT j = 0; j <= m_uiMeshWidth; ++j)
			{
				glm::vec3 stPos;
				stPos.x = m_stBoundBoxMin.x + j * fCellWidth;
				stPos.y = m_stBoundBoxMin.y + i * fCellHeight;
				stPos.z = pInterpolator->Interpolate(&glm::vec2(stPos.x, stPos.y));
				pTempVertexPos[uiTempVertexCount++] = stPos;
			}
		}
		delete pInterpolator;
	}

	//fill vertex buffer
	for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
	{
		//fill top face
		for(UINT i = 0; i <= m_uiMeshHeight; ++i)
		{
			for(UINT j = 0; j <= m_uiMeshWidth; ++j)
			{
				SRenderVertex sVertex;
				UINT uiIndex = uiStratum * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1) + i * (m_uiMeshWidth + 1) + j;
				sVertex.stPosition = glm::vec4(
					pTempVertexPos[uiIndex].x, 
					pTempVertexPos[uiIndex].y, 
					pTempVertexPos[uiIndex].z, 
					1.0f);
				float fDensity = GetStratumDensity(
					pPorosityInterpolator, 
					pSaturationInterpolator, 
					sVertex.stPosition);
				sVertex.stDiffuse.x = fDensity;
				m_fMaxDensity = glm::max(fDensity, m_fMaxDensity);
				m_pStratumVertexBuffer1[m_uiStratumVertexCount] = sVertex;
				m_pStratumVertexBuffer1[m_uiStratumVertexCount].stDiffuse = HSV(uiStratum * 360.0f / uiStratumCount, 1.0f, 1.0f);
				m_pStratumVertexBuffer2[m_uiStratumVertexCount] = sVertex;
				++m_uiStratumVertexCount;
			}
		}

		if(uiStratum == uiStratumCount - 1)break;

		//fill middle faces
		for(UINT k = 1; k < m_uiMeshDepth; ++k)
		{
			for(UINT i = 0; i <= m_uiMeshHeight; ++i)
			{
				for(UINT j = 0; j <= m_uiMeshWidth; ++j)
				{
					UINT uiIndex = uiStratum * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1) + i * (m_uiMeshWidth + 1) + j;
					UINT uiNextIndex = (uiStratum + 1) * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1) + i * (m_uiMeshWidth + 1) + j;
					float z = pTempVertexPos[uiIndex].z - k * (pTempVertexPos[uiIndex].z - pTempVertexPos[uiNextIndex].z) / m_uiMeshDepth;
					SRenderVertex sVertex;
					sVertex.stPosition = glm::vec4(
						m_stBoundBoxMin.x + j * fCellWidth, 
						m_stBoundBoxMin.y + i * fCellHeight, 
						z, 1.0f);
					float fDensity = GetStratumDensity(
						pPorosityInterpolator, 
						pSaturationInterpolator, 
						sVertex.stPosition);
					sVertex.stDiffuse.x = fDensity;
					m_fMaxDensity = glm::max(fDensity, m_fMaxDensity);
					m_pStratumVertexBuffer1[m_uiStratumVertexCount] = sVertex;
					m_pStratumVertexBuffer1[m_uiStratumVertexCount].stDiffuse = HSV(uiStratum * 360.0f / uiStratumCount, 1.0f, 1.0f);
					m_pStratumVertexBuffer2[m_uiStratumVertexCount] = sVertex;
					++m_uiStratumVertexCount;
				}
			}
		}

		//fill bottom face
		for(UINT i = 0; i <= m_uiMeshHeight; ++i)
		{
			for(UINT j = 0; j <= m_uiMeshWidth; ++j)
			{
				SRenderVertex sVertex;
				UINT uiIndex = (uiStratum + 1) * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1) + i * (m_uiMeshWidth + 1) + j;
				sVertex.stPosition = glm::vec4(
					pTempVertexPos[uiIndex].x, 
					pTempVertexPos[uiIndex].y, 
					pTempVertexPos[uiIndex].z, 
					1.0f);
				float fDensity = GetStratumDensity(
					pPorosityInterpolator, 
					pSaturationInterpolator, 
					sVertex.stPosition);
				sVertex.stDiffuse.x = fDensity;
				m_fMaxDensity = glm::max(fDensity, m_fMaxDensity);
				m_pStratumVertexBuffer1[m_uiStratumVertexCount] = sVertex;
				m_pStratumVertexBuffer1[m_uiStratumVertexCount].stDiffuse = HSV(uiStratum * 360.0f / uiStratumCount, 1.0f, 1.0f);
				m_pStratumVertexBuffer2[m_uiStratumVertexCount] = sVertex;
				++m_uiStratumVertexCount;
			}
		}
	}

	for(UINT i = 0; i < m_uiStratumVertexCount; ++i)
	{
		m_pStratumVertexBuffer2[i].stDiffuse = HSV(
			(1.0f - m_pStratumVertexBuffer2[i].stDiffuse.x / m_fMaxDensity) * 180.0f, 1.0f, 1.0f);
	}

	delete[] pTempVertexPos;

	delete pPorosityInterpolator;
	delete pSaturationInterpolator;
}

void CStratumRender::FillStratumModels()
{
	const CArray2D<glm::vec3>& cDrillData = *m_pDrillData;
	UINT uiDrillCount = cDrillData.GetHeight();
	UINT uiStratumCount = cDrillData.GetWidth();

	for(UINT uiStratum = 0; uiStratum < uiStratumCount; ++uiStratum)
	{
		SModel sModel;
		::ZeroMemory(&sModel, sizeof(sModel));

		UINT uiMeshStart = uiStratum * (m_uiMeshDepth + 1) * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);

		//layer mode
		sModel.pIndexBuffers[ERenderModeLayer] = new int[m_uiMeshHeight * m_uiMeshWidth * 6];
		#define STRATUM_LAYER_INDEX sModel.pIndexBuffers[ERenderModeLayer][sModel.uiIndexCounts[ERenderModeLayer]++]
		for(UINT i = 0; i < m_uiMeshHeight; ++i)
		{
			for(UINT j = 0; j < m_uiMeshWidth; ++j) 
			{
				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * i + j;
				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * (i + 1) + j;
				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * (i + 1) + (j + 1);

				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * i + j;
				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * (i + 1) + (j + 1);
				STRATUM_LAYER_INDEX = uiMeshStart + (m_uiMeshWidth + 1) * i + j + 1;
			}
		}

		if(uiStratum == uiStratumCount - 1)
		{
			m_cStratumModels.push_back(sModel);
			break;
		}

		//mesh mode
		sModel.pIndexBuffers[ERenderModeMesh] = new int[m_uiMeshDepth * m_uiMeshHeight * m_uiMeshWidth * 24];
		#define STRATUM_MESH_INDEX sModel.pIndexBuffers[ERenderModeMesh][sModel.uiIndexCounts[ERenderModeMesh]++]
		for(UINT k = 0; k < m_uiMeshDepth; ++k)
		{
			for(UINT i = 0; i < m_uiMeshHeight; ++i)
			{
				for(UINT j = 0; j < m_uiMeshWidth; ++j) 
				{
					UINT uiBackTopLeft = uiMeshStart + k * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1) + i * (m_uiMeshWidth + 1) + j;
					UINT uiBackTopRight = uiBackTopLeft + 1;
					UINT uiBackBottomLeft = uiBackTopLeft + (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
					UINT uiBackBottomRight = uiBackBottomLeft + 1;
					UINT uiFrontTopLeft = uiBackTopLeft + (m_uiMeshWidth + 1);
					UINT uiFrontTopRight = uiFrontTopLeft + 1;
					UINT uiFrontBottomLeft = uiFrontTopLeft + (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
					UINT uiFrontBottomRight = uiFrontBottomLeft + 1;

					STRATUM_MESH_INDEX = uiBackTopLeft;		STRATUM_MESH_INDEX = uiBackTopRight;
					STRATUM_MESH_INDEX = uiBackTopRight;	STRATUM_MESH_INDEX = uiFrontTopRight;
					STRATUM_MESH_INDEX = uiFrontTopRight;	STRATUM_MESH_INDEX = uiFrontTopLeft;
					STRATUM_MESH_INDEX = uiFrontTopLeft;	STRATUM_MESH_INDEX = uiBackTopLeft;

					STRATUM_MESH_INDEX = uiBackTopLeft;		STRATUM_MESH_INDEX = uiBackBottomLeft;
					STRATUM_MESH_INDEX = uiBackTopRight;	STRATUM_MESH_INDEX = uiBackBottomRight;
					STRATUM_MESH_INDEX = uiFrontTopRight;	STRATUM_MESH_INDEX = uiFrontBottomRight;
					STRATUM_MESH_INDEX = uiFrontTopLeft;	STRATUM_MESH_INDEX = uiFrontBottomLeft;

					STRATUM_MESH_INDEX = uiBackBottomLeft;	STRATUM_MESH_INDEX = uiBackBottomRight;
					STRATUM_MESH_INDEX = uiBackBottomRight;	STRATUM_MESH_INDEX = uiFrontBottomRight;
					STRATUM_MESH_INDEX = uiFrontBottomRight;STRATUM_MESH_INDEX = uiFrontBottomLeft;
					STRATUM_MESH_INDEX = uiFrontBottomLeft;	STRATUM_MESH_INDEX = uiBackBottomLeft;
				}
			}
		}

		//solid mode
		sModel.pIndexBuffers[ERenderModeSolid] = new int[
			(m_uiMeshWidth * m_uiMeshHeight) * 12 + 
			(m_uiMeshWidth * m_uiMeshDepth) * 12 + 
			(m_uiMeshHeight * m_uiMeshDepth) * 12];
		#define STRATUM_SOLID_INDEX sModel.pIndexBuffers[ERenderModeSolid][sModel.uiIndexCounts[ERenderModeSolid]++]
		
		for(UINT nSide = 0; nSide < 6; ++nSide)
		{
			UINT uiHeight, uiWidth, uiHeightPitch, uiWidthPitch, uiVertexStart;
			switch(nSide)
			{
			case 0: //top
				uiHeight = m_uiMeshHeight + 1;
				uiWidth = m_uiMeshWidth + 1;
				uiHeightPitch = m_uiMeshWidth + 1;
				uiWidthPitch = 1;
				uiVertexStart = uiMeshStart;
				break;
			case 1: //bottom
				uiHeight = m_uiMeshHeight + 1;
				uiWidth = m_uiMeshWidth + 1;
				uiHeightPitch = m_uiMeshWidth + 1;
				uiWidthPitch = 1;
				uiVertexStart = uiMeshStart + m_uiMeshDepth * (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
				break;
			case 2: //back
				uiHeight = m_uiMeshDepth + 1;
				uiWidth = m_uiMeshWidth + 1;
				uiHeightPitch = (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
				uiWidthPitch = 1;
				uiVertexStart = uiMeshStart;
				break;
			case 3: //front
				uiHeight = m_uiMeshDepth + 1;
				uiWidth = m_uiMeshWidth + 1;
				uiHeightPitch = (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
				uiWidthPitch = 1;
				uiVertexStart = uiMeshStart + m_uiMeshHeight * (m_uiMeshWidth + 1);
				break;
			case 4: //left
				uiHeight = m_uiMeshDepth + 1;
				uiWidth = m_uiMeshHeight + 1;
				uiHeightPitch = (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
				uiWidthPitch = m_uiMeshWidth + 1;
				uiVertexStart = uiMeshStart;
				break;
			case 5: //right
				uiHeight = m_uiMeshDepth + 1;
				uiWidth = m_uiMeshHeight + 1;
				uiHeightPitch = (m_uiMeshHeight + 1) * (m_uiMeshWidth + 1);
				uiWidthPitch = m_uiMeshWidth + 1;
				uiVertexStart = uiMeshStart + m_uiMeshWidth;
				break;
			}

			for(UINT i = 0; i < uiHeight - 1; ++i)
			{
				for(UINT j = 0; j < uiWidth - 1; ++j) 
				{
					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * i + j * uiWidthPitch;
					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * (i + 1) + j * uiWidthPitch;
					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * (i + 1) + (j + 1) * uiWidthPitch;

					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * i + j * uiWidthPitch;
					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * (i + 1) + (j + 1) * uiWidthPitch;
					STRATUM_SOLID_INDEX = uiVertexStart + uiHeightPitch * i + (j + 1) * uiWidthPitch;
				}
			}
		}

		m_cStratumModels.push_back(sModel);
	}
}

IInterpolator* CStratumRender::CreateInterpolator()
{
	switch(m_eInterpolateMode)
	{
	case EInterpolateModeIDW:
		return new CIDWInterpolator();
		break;
	case EInterpolateModeKriging:
		return new CKrigingInterpolator();
		break;
	}

	return NULL;
}

void CStratumRender::DestroyModel()
{
	if(m_pDrillVertexBuffer)
	{
		delete[] m_pDrillVertexBuffer;
		m_pDrillVertexBuffer = NULL;
	}
	for(std::vector<SModel>::iterator it = m_cDrillModels.begin(); 
		it != m_cDrillModels.end(); ++it)
	{
		for(int i  = 0; i < ERenderModeCount; ++i)
		{
			delete[] it->pIndexBuffers[i];
		}
	}
	m_cDrillModels.clear();

	if(m_pStratumVertexBuffer1)
	{
		delete[] m_pStratumVertexBuffer1;
		m_pStratumVertexBuffer1 = NULL;
	}
	if(m_pStratumVertexBuffer2)
	{
		delete[] m_pStratumVertexBuffer2;
		m_pStratumVertexBuffer2 = NULL;
	}
	for(std::vector<SModel>::iterator it = m_cStratumModels.begin(); 
		it != m_cStratumModels.end(); ++it)
	{
		for(int i  = 0; i < ERenderModeCount; ++i)
		{
			delete[] it->pIndexBuffers[i];
		}
	}
	m_cStratumModels.clear();
}

float CStratumRender::GetStratumDensity(
	IInterpolator* pPorosityInterpolator, 
	IInterpolator* pSaturationInterpolator, 
	glm::vec4& stPosition)
{
	float fPorosity = pPorosityInterpolator->Interpolate(
		&glm::vec3(
		stPosition.x, 
		stPosition.y, 
		stPosition.z));
	fPorosity = glm::max(glm::min(fPorosity, 1.0f), 0.0f);
	float fSaturation = pSaturationInterpolator->Interpolate(
		&glm::vec3(
		stPosition.x, 
		stPosition.y, 
		stPosition.z));
	fSaturation = glm::max(glm::min(fSaturation, 1.0f), 0.0f);
	return fPorosity * fSaturation;
}

void CStratumRender::RenderBoundBox()
{
	glm::vec4 stBorderColor(0.5f, 0.5f, 0.5f, 1.0f);
	SRenderVertex stBoundBoxVertexs[] = 
	{
		{ glm::vec4(m_stBoundBoxMin.x, m_stBoundBoxMin.y, m_stBoundBoxMin.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMin.x, m_stBoundBoxMax.y, m_stBoundBoxMin.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMax.x, m_stBoundBoxMax.y, m_stBoundBoxMin.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMax.x, m_stBoundBoxMin.y, m_stBoundBoxMin.z, 1), stBorderColor }, 

		{ glm::vec4(m_stBoundBoxMin.x, m_stBoundBoxMin.y, m_stBoundBoxMax.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMin.x, m_stBoundBoxMax.y, m_stBoundBoxMax.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMax.x, m_stBoundBoxMax.y, m_stBoundBoxMax.z, 1), stBorderColor }, 
		{ glm::vec4(m_stBoundBoxMax.x, m_stBoundBoxMin.y, m_stBoundBoxMax.z, 1), stBorderColor }
	};
	UINT uiBoundBoxIndices[] = 
	{
		0, 1, 1, 2, 2, 3, 3, 0, 
		4, 5, 5, 6, 6, 7, 7, 4, 
		0, 1, 1, 5, 5, 4, 4, 0, 
		3, 2, 2, 6, 6, 7, 7, 3, 
		1, 5, 5, 6, 6, 2, 2, 1, 
		0, 4, 4, 7, 7, 3, 3, 0
	};
	glVertexAttribPointer(
		m_pRenderContext->GetPositionAttribLocation(), 
		4, GL_FLOAT, false, sizeof(SRenderVertex), 
		stBoundBoxVertexs);
	glVertexAttribPointer(
		m_pRenderContext->GetDiffuseAtrribLocation(), 
		4, GL_FLOAT, false, sizeof(SRenderVertex), 
		((float*)stBoundBoxVertexs) + 4);
	glDrawElements(
		GL_LINES, 
		48, 
		GL_UNSIGNED_INT, 
		uiBoundBoxIndices);
}

void CStratumRender::RenderLegend()
{
	HWND hPreviewWnd = m_pRenderContext->GetPreviewWnd();
	RECT rcPreview;
	int nWidth, nHeight;
	::GetClientRect(hPreviewWnd, &rcPreview);
	nWidth = rcPreview.right - rcPreview.left;
	nHeight = rcPreview.bottom - rcPreview.top;

	glm::vec3 stCenter(0, 0, 0);
	glm::vec3 stEye(0, 0, 1);
	glm::vec3 stUp(0, 1, 0);
	glm::mat4 stView = glm::lookAt(stEye, stCenter, stUp);
	glm::mat4 stProjection = glm::ortho(
		-nWidth / 2.0f, 
		nWidth / 2.0f, 
		-nHeight / 2.0f, 
		nHeight / 2.0f);

	glm::mat4 stWorldViewProj = stProjection * stView;

	//render legend
	const int nCount = 10;
	for(int i = 0; i < nCount; ++i)
	{
		float fDensity = i * m_fMaxDensity / (nCount - 1);
		glm::vec4 stColor = HSV((1.0f - fDensity / m_fMaxDensity) * 180.0f, 1.0f, 1.0f);
		float fLeft = nWidth / 2.0f - 100.0f;
		float fRight = fLeft + 25.0f;
		float fTop = nHeight / 2.0f - 5.0f - i * 20.0f;
		float fBottom = fTop - 15.0f;
		SRenderVertex stVertexArray[] = 
		{
			{ glm::vec4(fLeft, fTop, 0, 1), stColor }, 
			{ glm::vec4(fRight, fTop, 0, 1), stColor }, 
			{ glm::vec4(fRight, fBottom, 0, 1), stColor }, 
			{ glm::vec4(fLeft, fBottom, 0, 1), stColor }
		};
		UINT uiIndexArray[] = { 0, 1, 2, 3 };

		m_pRenderContext->Begin(stWorldViewProj);

		glShadeModel(GL_SMOOTH);
		glDisable(GL_CULL_FACE);
		glDisable(GL_BLEND);
		glEnableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
		glEnableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());
		glVertexAttribPointer(
			m_pRenderContext->GetPositionAttribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), 
			stVertexArray);
		glVertexAttribPointer(
			m_pRenderContext->GetDiffuseAtrribLocation(), 
			4, GL_FLOAT, false, sizeof(SRenderVertex), 
			((float*)stVertexArray) + 4);
		glDrawElements(
			GL_QUADS, 
			4, 
			GL_UNSIGNED_INT, 
			uiIndexArray);
		glDisableVertexAttribArray(m_pRenderContext->GetPositionAttribLocation());
		glDisableVertexAttribArray(m_pRenderContext->GetDiffuseAtrribLocation());

		m_pRenderContext->End();
		
		exstring strValue;
		strValue.format("%.3f", fDensity);
		for(size_t j = 0; j < strValue.size(); ++j)
		{
			glWindowPos2f(fRight + nWidth / 2.0f + 3.0f + 8.0f * j, fBottom + nHeight / 2.0f + 3.0f);
			glutBitmapCharacter(GLUT_BITMAP_8_BY_13, strValue[j]);
		}
	}
}